<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Libs\Frame;

use Srv\Libs\Storage\DataMysql;
use Srv\Libs\Tools\ReflectionClassData;

abstract class DBOAbstract extends OAbstract
{
    private static array $refFieldCache                 = [];       // 字段数据缓存

    /**
     * @param string $dbName
     * @return string
     * getTableName
     */
    final public static function getTableName(string &$dbName = ''):string
    {
        return ReflectionClassData::getRefTableNameByProto(static::class, $dbName);
    }

    /**
     * @return string
     * getTableNameAll
     */
    final public static function getTableNameAll():string
    {
        $dbName     = '';
        $tableName  = self::getTableName($dbName);
        return '`'.$dbName.'`.`'.$tableName.'`';
    }

    /**
     * @param string $tableName
     * @param string $dbName
     * @return array
     * getFieldDataList
     */
    final public static function getFieldDataList(string &$tableName = '', string &$dbName = ''):array
    {
        return self::getRefFieldByProtoData($tableName, $dbName);
    }

    /**
     * @param string $whereStr
     * @param DataMysql|null $DbAuto
     * @return static|null
     * getDboOne
     */
    final public static function getDboOne(string $whereStr, ?DataMysql $DbAuto = null):?self
    {
        if(is_null($DbAuto)) $DbAuto = ControllerAbstract::getDbAuto();
        $tableName          = '';
        $dbName             = '';
        $propertyList       = self::getRefFieldByProtoData($tableName, $dbName);
        if(strlen($tableName) < 1 || strlen($dbName) < 1 || count($propertyList) < 1) return null;
        $columnNameList     = [];
        foreach ($propertyList as $propertyItem){
            $columnName     = $propertyItem['columnName']??'';
            if(strlen($columnName) < 1) continue;
            $columnNameList[$columnName] = $propertyItem;
        }
        if(count($columnNameList) < 1) return null;
        $isFail             = false;
        $dataItem           = $DbAuto->getDataOne('SELECT '.implode(', ', array_keys($columnNameList)).' FROM `'.$dbName.'`.`'.$tableName.'`'.(strlen($whereStr) > 0 ? ' WHERE '.$whereStr : ''), $isFail);
        if($isFail || count($dataItem) < 1) return null;
        $dataValue          = [];
        foreach ($dataItem as $fieldName => $fieldValue){
            if(!isset($columnNameList[$fieldName])) continue;
            $name               = $columnNameList[$fieldName]['name']??'';
            if(strlen($name) < 1) continue;
            $dataValue[$name]   = self::getDataFieldValue(strtolower($columnNameList[$fieldName]['columnType']??''), $fieldValue);
        }
        return count($dataValue) > 0 ? new static($dataValue) : null;
    }

    /**
     * @param string $whereStr
     * @param DataMysql|null $DbAuto
     * @return array
     * getDboList
     */
    final public static function getDboList(string $whereStr, ?DataMysql $DbAuto = null):array
    {
        if(is_null($DbAuto)) $DbAuto = ControllerAbstract::getDbAuto();
        $dboList            = [];
        $tableName          = '';
        $dbName             = '';
        $propertyList       = self::getRefFieldByProtoData($tableName, $dbName);
        if(strlen($tableName) < 1 || strlen($dbName) < 1 || count($propertyList) < 1) return $dboList;
        $columnNameList     = [];
        foreach ($propertyList as $propertyItem){
            $columnName     = $propertyItem['columnName']??'';
            if(strlen($columnName) < 1) continue;
            $columnNameList[$columnName] = $propertyItem;
        }
        if(count($columnNameList) < 1) return $dboList;
        $isFail             = false;
        $dataList           = $DbAuto->getData('SELECT '.implode(', ', array_keys($columnNameList)).' FROM `'.$dbName.'`.`'.$tableName.'`'.(strlen($whereStr) > 0 ? ' WHERE '.$whereStr : ''), $isFail);
        if($isFail || count($dataList) < 1) return $dboList;
        foreach ($dataList as $dataItem){
            if(is_array($dataItem) && count($dataItem) > 0){
                $dataValue              = [];
                foreach ($dataItem as $fieldName => $fieldValue){
                    if(!isset($columnNameList[$fieldName])) continue;
                    $name               = $columnNameList[$fieldName]['name']??'';
                    if(strlen($name) < 1) continue;
                    $dataValue[$name]   = self::getDataFieldValue(strtolower($columnNameList[$fieldName]['columnType']??''), $fieldValue);
                }
                if(count($dataValue) > 0) $dboList[] = new static($dataValue);
            }
        }
        return $dboList;
    }

    /**
     * @param string $tableName
     * @param string $dbName
     * @return array
     * getRefFieldByProtoData
     */
    private static function getRefFieldByProtoData(string &$tableName = '', string &$dbName = ''):array
    {
        if(!isset(self::$refFieldCache[static::class])){
            $propertyList       = ReflectionClassData::getRefFieldByProto(static::class, $tableName, $dbName);
            self::$refFieldCache[static::class] = [$tableName, $dbName, $propertyList];
        }else{
            $tableName          = self::$refFieldCache[static::class][0];
            $dbName             = self::$refFieldCache[static::class][1];
            $propertyList       = self::$refFieldCache[static::class][2];
        }
        return $propertyList;
    }

    /**
     * @param $columnType
     * @param $fieldValue
     * @return bool|float|int
     * getDataFieldValue
     */
    private static function getDataFieldValue($columnType, $fieldValue)
    {
        switch ($columnType){
            case 'bit':
            case 'tinyint':
            case 'smallint':
            case 'mediumint':
            case 'int':
            case 'timestamp':
            case 'serial':
            case 'bigint': {
                return intval($fieldValue);
            }
            case 'decimal':
            case 'float':
            case 'double':
            case 'real':{
                return floatval($fieldValue);
            }
            case 'boolean':{
                return boolval($fieldValue);
            }
            default:{
                return $fieldValue;
            }
        }
    }
}